using System;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;

/*	-----------------------------------------------------------------------	
	Copyright:	umlungu consulting (Pty) ltd
	Author:		Alan Benington	
	Started:	2005-05-15	
	Status:		release	
	Version:	2.0.0
	Buidl:		20070611
	License:	GNU General Public License
	-----------------------------------------------------------------------	*/

/*	---------------------------------------------------------------------------	
	Development Notes:
	==================
	20070524:	Modification for .Net 2.0
	20070611:	Fixed bug in destructor
	20070703:	Removed net_2_0 directive (from compatibility build)
	20071022:	Added image type of blogspace
	---------------------------------------------------------------------------	*/

namespace umlungu.engineX.blogX {
	/// <summary>
	/// umlungu engineX blogspace blogspace class.
	/// </summary>
	public class x_blogspace {
		#region Invisible properties
		private HttpApplicationState _application;
		private string _path;
		private XmlDocument _blogspace, _users;
		private x_blogspaceType _type;
		private XmlNode _blogspaces;
		private XmlDocument _root;
		#endregion

		#region Visible properties
		/// <summary>The blogspaces node</summary>
		/// <value>XmlNode labelled 'blogspaces'</value>
		public XmlNode Blogspaces {	
			get	{ return _root.SelectSingleNode("//blogspaces"); }	
		}
		/// <summary>Blogspace document root</summary>
		/// <value>The DocumentElement of the xml doc</value>
		public XmlNode Root	{
			get	{ return _root.DocumentElement;	}
		}
		/// <summary>The name of the blogspace</summary>
		/// <value>The name attribute</value>
		public string Name	{
			get	{ return _blogspace.DocumentElement.GetAttribute("name");}
		}
		private XmlElement blogspace;
		/// <summary>The blogspace XmlElement</summary>
		/// <value>An XmlElement containing the blogspace nodes</value>
		public XmlElement Blogspace	{
			get	{	return blogspace;	}	
			set	{	blogspace = value;	}
		}
		/// <summary>Path to blogspaces root directory</summary>
		/// <value>string containing the local path to the 'Blogs' root</value>
		public string Path {
			get { return _path + "/Blogs/"; }
		}
		#endregion
		
		#region Constructors/Destructors
		/// <overloads>Constructor</overloads>
		/// <summary>Constructor</summary>
		/// <param name="application">Web application object</param>
		/// <param name="name">Blogspace name</param>
		/// <param name="topic">Blogspace topic</param>
		public x_blogspace(HttpApplicationState application, string name, string topic) {
			initialise(application);
			Archive(name, topic, false);
		}
		/// <summary>Constructor</summary>
		/// <param name="application">Web application object</param>
		/// <param name="name">Blogspace name</param>
		public x_blogspace(HttpApplicationState application, string name) {
			initialise(application);
			_Load(name);
		}
		/// <summary>Constructor</summary>
		/// <param name="application">Web application object</param>
		public x_blogspace(HttpApplicationState application) {
			initialise(application);
		}
		/// <summary>Destructor</summary>
		~x_blogspace() {
			if (blogspace != null && _blogspaces != null) {
				if (_blogspaces.Equals(blogspace.ParentNode))
					_blogspaces.RemoveChild(blogspace);
			}
		}
		#endregion

		#region Public methods
		/// <summary>
		/// Gets a blogspace.
		/// </summary>
		public void Get(string name) {
			_Load(name);
			addUsers();
		}
		
		/// <summary>
		/// Add blogspace.
		/// </summary>
		public void Add(string name, string type, string title, string text, string admin, bool isactive, bool istopics) {
			if (_root.SelectSingleNode("//blogspaces/blogspace[@name='"+name+"']") != null)
				throw(new x_exception("error_duplicate", "blogspace with this name exists"));
			if (_users.SelectSingleNode("//users/user[@name='"+admin+"']") == null)
				throw(new x_exception("error_userexist", "user does not exist"));
			if (_users.SelectSingleNode("//users/user[@name='"+admin+"'][@group='blogadmin' or @group='siteadmin']") == null)
				throw(new x_exception("error_admin", String.Concat("user '", admin, "' is not a site or blogspace administrator")));
			
			_blogspace = new XmlDocument();
			_blogspace.LoadXml("<blogX>" + _root.SelectSingleNode("//default/blogspace").InnerXml + "</blogX>");
			// now add the stuff to the blog's config file...
			XmlElement userel = (XmlElement) _blogspace.SelectSingleNode("//users/user");
			userel.RemoveAll();	// remove all child nodes and attributes than only add ones required
			userel.SetAttribute("name", admin);
			blogspace = _blogspace.DocumentElement;
			blogspace.SetAttribute("name", name);
			setType(type);
			blogspace.SetAttribute("admin", admin);
			blogspace.SetAttribute("active", (isactive)? "yes" : "no");
			blogspace.SetAttribute("template", "yes");	// default 
			_blogspace.SelectSingleNode("//title").InnerText = title.Replace("%20", " ");
			_blogspace.SelectSingleNode("//text").InnerText = text.Replace("%20", " ");
			// add blogspace to root
			XmlElement space = _root.CreateElement("blogspace");
			space.SetAttribute("name", name);
			setType(space, type);
			space.SetAttribute("active", (isactive)? "yes" : "no");
			space.SetAttribute("topics", (istopics)? "yes" : "no");
			space.InnerText = title.Replace("%20", " ");
			_root.SelectSingleNode("//blogspaces").AppendChild(space);
			_root.Save(pathRoot(name));
			// Create blogspace directories and save files
			Directory.CreateDirectory(_path + "/Blogs/" + name);
			Directory.CreateDirectory(_path + "/Blogs/" + name + "/archive");
			_blogspace.Save(pathSpace(name));
			// Add default topic
			AddTopic(name, "default", "Default topic", "text");
		}
		
		/// <summary>
		/// Delete blogspace by marking inactive.
		/// </summary>
		public void Delete(string name) {
			_Load(name);
			_blogspace.DocumentElement.SetAttribute("active", "no");
			_blogspace.Save(pathSpace(name));
			// set active attribute in site config...
			XmlElement blogel = (XmlElement) _root.SelectSingleNode("//blogspaces/blogspace[@name='"+name+"']");
			blogel.SetAttribute("active", "no");
			_root.Save(pathRoot(name));
		}
		
		/// <summary>
		/// Undelete blogspace by marking active.
		/// </summary>
		public void Undelete(string name) {
			_ForceLoad(name);
			_blogspace.DocumentElement.SetAttribute("active", "yes");
			_blogspace.Save(pathSpace(name));
			// set active attribute in site config...
			XmlElement blogel = (XmlElement) _root.SelectSingleNode("//blogspaces/blogspace[@name='"+name+"']");
			blogel.SetAttribute("active", "yes");
			_root.Save(pathRoot(name));
		}
		
		/// <summary>
		/// Remove blogspace by moving to "Deleted".
		/// </summary>
		public void Remove(string name) {
			// remove in site config...
			XmlNode thisblog = _root.SelectSingleNode("//blogspaces/blogspace[@name='"+name+"']");
			if (thisblog == null)
				throw(new x_exception("error_spaceexist", String.Concat("blogspace '", name, "'does not exist")));
			thisblog.ParentNode.RemoveChild(thisblog);
			_root.Save(pathRoot(name));
			// Move directory to 'Deleted'...
			Directory.Move(_path + "/Blogs/" + name, _path + "/Deleted/" + name);
		}
		
		/// <summary>
		/// Update existing blogspace.
		/// </summary>
		public void Update(string name, string type, string title, string text, bool istopics) {
			_Load(name);
			blogspace.SelectSingleNode("/blogspace/title").InnerText = title.Replace("%20", " ");
			blogspace.SelectSingleNode("/blogspace/text").InnerText = text.Replace("%20", " ");
			XmlElement space = (XmlElement) blogspace.SelectSingleNode("/blogspace");
			setType(space, type);
			_blogspace.Save(pathSpace(name));
			// add blogspace to root
			space = (XmlElement) _root.SelectSingleNode("//blogspaces/blogspace[@name='"+name+"']");
			setType(space, type);
			space.SetAttribute("topics", (istopics)? "yes" : "no");
			space.InnerText = title;
			_root.Save(pathRoot(name));
		}
		
		/// <summary>
		/// Update existing blogspace.
		/// </summary>
		public void Update(string name, string type, string title, string text) {
			_Load(name);
			blogspace.SelectSingleNode("/blogspace/title").InnerText = title.Replace("%20", " ");
			blogspace.SelectSingleNode("/blogspace/text").InnerText = text.Replace("%20", " ");
			setType(type);
			_blogspace.Save(pathSpace(name));
			// add blogspace to root
			XmlElement space = (XmlElement) _root.SelectSingleNode("//blogspaces/blogspace[@name='"+name+"']");
			setType(space, type);
			space.InnerText = title;
			_root.Save(pathRoot(name));
		}
		
		/// <summary>
		/// Lists blogspaces (in a Blogdomain) with a list of archives.
		/// </summary>
		public void List() {
			// all done in initialise?
		}
		
		/// <overloads>Add a user to a blogspace.</overloads>
		/// <summary>User with specified profile</summary>
		public void AddUser(string name, string user, string group) {
			XmlElement _userel = (XmlElement) _users.SelectSingleNode("//users/user[@name='"+user+"']");
			if (_userel == null)
				throw(new x_exception("error_userexist", String.Concat("user '", user, "' does not exist")));
			_Load(name);
			if (blogspace.SelectSingleNode("//users/user[@name='"+user+"']") != null)
				throw(new x_exception("error_exists", String.Concat("user with name '", user, "' exists")));
			XmlElement userel = _blogspace.CreateElement("user");
			userel.SetAttribute("name", user);
			userel.InnerText = String.Concat(_userel.SelectSingleNode("firstname").InnerText, " ", _userel.SelectSingleNode("surname").InnerText);
			if (group != "")
				userel.SetAttribute("group", group);
			blogspace.SelectSingleNode("//users").AppendChild(userel);
			_blogspace.Save(pathSpace(name));
		}
		/// <summary>Default profile</summary>
		public void AddUser(string name, string user) {
			XmlElement _userel = (XmlElement) _users.SelectSingleNode("//users/user[@name='"+user+"']");
			if (_userel == null)
				throw(new x_exception("error_userexist", String.Concat("user '", user, "' does not exist")));
			_Load(name);
			if (blogspace.SelectSingleNode("//users/user[@name='"+user+"']") != null)
				throw(new x_exception("error_exists", String.Concat("user with name '", user, "' exists")));
			XmlElement userel = _blogspace.CreateElement("user");
			userel.SetAttribute("name", user);
			userel.InnerText = String.Concat(_userel.SelectSingleNode("firstname").InnerText, " ", _userel.SelectSingleNode("surname").InnerText);
			blogspace.SelectSingleNode("//users").AppendChild(userel);
			_blogspace.Save(pathSpace(name));
		}
		
		/// <summary>
		/// Delete a user from a blogspace.
		/// </summary>
		public void DeleteUser(string name, string user) {
			if (_users.SelectSingleNode("//users/user[@name='"+user+"']") == null)
				throw(new x_exception("error_userexist", "user does not exist"));
			_Load(name);
			if (blogspace.GetAttribute("admin") == user)
				throw(new x_exception("error_delete", "cannot delete blogspace admin"));
			XmlNode userel = blogspace.SelectSingleNode("//users/user[@name='"+user+"']");
			if (userel != null)
				userel.ParentNode.RemoveChild(userel);
			_blogspace.Save(pathSpace(name));
		}
		
		/// <summary>
		/// Gets a blogspace's template.
		/// </summary>
		public void GetTemplate(string name) {
			_Load(name);
			if (blogspace.GetAttribute("template") != "yes")
				throw(new x_exception("error_template", "blogspace template does not exist"));
			getTemplate(name);
		}

		/// <overloads>Gets a blogspace's template as a XslTransform object.</overloads>
		/// <summary>Blogspace name supplied</summary>
		public XslCompiledTransform GetTransform(string name) {
			_Load(name);
			return GetTransform();
		}
		/// <summary>Blogspace loaded</summary>
		public XslCompiledTransform GetTransform() {
			if (_blogspace == null)
				throw(new x_exception("error_loadblogspace", "blogspace not loaded"));
			if (blogspace.GetAttribute("template") != "yes")
				throw(new x_exception("error_template", "blogspace template does not exist"));
			XslCompiledTransform templ = new XslCompiledTransform();
			templ.Load(pathTemplate(Name));
			return(templ);
		}
		
		/// <summary>
		/// SetTemplate saves the xsl to the default template file.
		/// </summary>
		public void SetTemplate(string name, string templxsl) {
			_Load(name);
			XmlDocument templxml = new XmlDocument();
			templxml.LoadXml(templxsl);
			templxml.Save(pathTemplate(name));
			blogspace.SetAttribute("template", "yes");
			_blogspace.Save(pathSpace(name));
		}
		
		/// <summary>
		/// Adds a new topic to the blogspace (and a blog file).
		/// </summary>
		public void AddTopic(string name, string topic, string title, string type) {
			// check and add topic to topics list in config
			_Load(name);
			checkTopic(topic);
			addTopic(name, topic, title);
			addTopicFile(name, topic, getType(type));
		}
		/// Assumes _Load has been called
		private void addTopic(string name, string topic, string title) {
			// check and add topic to topics list in config
			XmlNode topicsnode = _blogspace.SelectSingleNode("//topics");
			XmlElement topicelem = _blogspace.CreateElement("topic");
			topicelem.InnerText = title;
			topicelem.SetAttribute("name", topic);
			topicsnode.AppendChild(topicelem);
			_blogspace.Save(pathSpace(name));
		}
		/// Creates topic file
		private void addTopicFile(string name, string topic, string type) {
			XmlDocument newtopic = new XmlDocument();
			newtopic.LoadXml(_root.SelectSingleNode("//default/topic").InnerXml);
			XmlElement defel = (XmlElement) newtopic.SelectSingleNode("//blogs");
			setType(defel, type);
			defel.SetAttribute("topic", topic);
			// set date attribute
			DateTime dt = DateTime.Now;
			defel.SetAttribute("date", dt.ToString("s", DateTimeFormatInfo.InvariantInfo));
			newtopic.Save(pathTopic(name, topic));
		}
		
		/// <summary>
		/// Edits an existing topic.
		/// </summary>
		public void EditTopic(string name, string topic, string title, string type) {
			// check and add topic to topics list in config
			_Load(name);
			checkisTopic(topic);
			XmlNode topicnode = _blogspace.SelectSingleNode("//topics/topic[@name='"+topic+"']");
			topicnode.InnerText = title.Replace("%20", " ");
			_blogspace.Save(pathSpace(name));
			// Update topic file
			XmlDocument newtopic = new XmlDocument();
			newtopic.Load(pathTopic(name, topic));
			XmlElement defel = (XmlElement) newtopic.SelectSingleNode("//blogs");
			setType(defel, type);
			defel.SetAttribute("topic", topic);
			newtopic.Save(pathTopic(name, topic));
		}
		
		/// <summary>
		/// Deletes a topic from the blogspace (archives it first then removes the reference in the blogspace).
		/// </summary>
		public void DeleteTopic(string name, string topic) {
			Archive(name, topic, true);
			removeTopic(topic);
			_blogspace.Save(pathSpace(name));
		}
		
		/// <summary>
		/// Archives the blog file and creates a new current blog.
		/// </summary>
		public void Archive(string name, string topic, bool force) {
			_Load(name);
			checkisTopic(topic);
			XmlElement archivesEl = (XmlElement) _blogspace.SelectSingleNode("//archives");
			string archivetype = archivesEl.GetAttribute("type");
			x_blog topicblog = new x_blog(_application, name, topic);
			string topictype = topicblog.Type;
			string archname = topicblog.Archive(archivetype, force);
			if (archname != "") {
				addArchive(topic, archname);
				addTopicFile(name, topic, topictype);
				_blogspace.Save(pathSpace(name));
			}
		}
		
		/// <summary>
		/// GetArchive: Get the type of archive set for the blog.
		/// </summary>
		public string GetArchive(string name) {
			_Load(name);
			XmlElement archivesEl = (XmlElement) _blogspace.SelectSingleNode("//archives");
			return(archivesEl.GetAttribute("type"));
		}
		
		/// <summary>
		/// ChangeArchive: Changes the type of archive.
		/// </summary>
		public void ChangeArchive(string name, string type) {
			_Load(name);
			XmlElement archivesEl = (XmlElement) _blogspace.SelectSingleNode("//archives");
			archivesEl.SetAttribute("type", type);
			_blogspace.Save(pathSpace(name));
		}
		
		/// <summary>
		/// Gets the archives for the blogspace.
		/// </summary>
		public void Archives(string name) {
			x_blog blogx = new x_blog(_application);
			_Load(name);
			XmlNodeList topics = _blogspace.SelectNodes("//archives/topic");
			XmlElement topicel;
			foreach(XmlNode topic in topics) {
				topicel = (XmlElement) topic;
				XmlNodeList archives = topic.SelectNodes("archive");
				XmlElement archiveel;
				foreach(XmlNode archive in archives) {
					archiveel = (XmlElement) archive;
					try {
						blogx.Gets(name, topicel.GetAttribute("name"), archiveel.GetAttribute("name"), "");
						archiveel.InnerXml = blogx.Blogs.OuterXml;
					} catch (x_exception e) {	// archives may exist for topics that have been deleted
						if (e.Code != "error_topicexist")
							throw(e);
					}
				}
			}
		}
		
		/// <overloads>Gets all the current blogs</overloads>
		/// <summary>For the blogspace.</summary>
		public void Current(string name) {
			Current(name, null, false);
		}
		/// <summary>For current topic</summary>
		public void Current(string name, string topic) {
			Current(name, topic, false);
		}
		/// <summary>For current topic with option of title only</summary>
		public void Current(string name, string topicName, bool titleOnly) {
			x_blog blogx = new x_blog(_application);
			_Load(name);
			XmlNode blogs = _blogspace.SelectSingleNode("//blogs");
			if (blogs != null) {
				XmlNode current = blogs.AppendChild(_blogspace.CreateElement("current"));
				XmlNode unsorted = blogs.AppendChild(_blogspace.CreateElement("unsorted"));
				XmlNodeList topics = (topicName != null && topicName != "")? _blogspace.SelectNodes(String.Concat("//topics/topic[@name='", topicName, "']")) : _blogspace.SelectNodes("//topics/topic");
				XmlElement topicel;
				string topicname;
				foreach(XmlNode topic in topics) {
					topicel = (XmlElement) topic;
					topicname = topicel.GetAttribute("name");
					blogx.Gets(name, topicname, "");
					XmlNodeList bloglist = blogx.Blogs.SelectNodes("//blogs/blog");
					topicel.SetAttribute("count", bloglist.Count.ToString());
					XmlElement currentblog, blogel;
					foreach(XmlNode blog in bloglist) {
						blogel = (XmlElement) blog;
						currentblog = addBlog(blogel, topicname);
						currentblog.InnerXml = (titleOnly)? blogel.SelectSingleNode("title").OuterXml : blogel.InnerXml;
						unsorted.AppendChild(currentblog);
					}
				}
				// sort and save the bloglist
				sortBlogs(current, "//blogs/unsorted/blog", "@date");
				XmlNodeList currlist = current.SelectNodes("blog");
				XmlNode unsort;
				XmlElement currel;
				foreach(XmlNode curr in currlist) {
					currel = (XmlElement) curr;
					unsort = unsorted.SelectSingleNode("blog[@date='"+currel.GetAttribute("date")+"']");
					currel.InnerXml = unsort.InnerXml;
				}
				blogs.RemoveChild(unsorted);
			}
		}
		/// <summary>Blogspace already loaded with option of title only.</summary>
		public void Current(bool titleOnly) {
			if (_blogspace == null)
				throw(new x_exception("error_loadblogspace", "blogspace not loaded"));
			x_blog blogx = new x_blog(_application);
			XmlNodeList topics = _blogspace.SelectNodes("//topics/topic");
			XmlElement topicel;
			string topicname;
			foreach(XmlNode topic in topics) {
				topicel = (XmlElement) topic;
				topicname = topicel.GetAttribute("name");
				blogx.Gets(Name, topicname, "");
				XmlNodeList bloglist = blogx.Blogs.SelectNodes("//blogs/blog");
				XmlElement currentblog, blogel;
				foreach(XmlNode blog in bloglist) {
					blogel = (XmlElement) blog;
					currentblog = addBlog(blogel, topicname);
					currentblog.InnerXml = (titleOnly)? blogel.SelectSingleNode("title").OuterXml : blogel.InnerXml;
					topicel.AppendChild(currentblog);
				}
			}
		}
		
		/// <overloads>Get the most recent blogs and creates a sorted list (by blog date count) in the blogspace.></overloads>
		/// <summary>Blogspace already loaded</summary>
		public void Recent() {
			Recent(Name);
		}
		/// <summary>Load blogspace</summary>
		public void Recent(string name) {
			x_blog blogx = new x_blog(_application);
			_Load(name);
			XmlNode recent = _blogspace.SelectSingleNode("//blogs/recent");
			if (recent == null)	// add it if it doesn't exist
				recent = _blogspace.SelectSingleNode("//blogs").AppendChild(_blogspace.CreateElement("recent"));
			recent.RemoveAll();
			XmlNode unsorted = recent.AppendChild(_blogspace.CreateElement("unsorted"));
			XmlNodeList topics = _blogspace.SelectNodes("//topics/topic");
			XmlElement topicel;
			string topicname;
			foreach(XmlNode topic in topics) {
				topicel = (XmlElement) topic;
				topicname = topicel.GetAttribute("name");
				blogx.Gets(name, topicname, "");
				XmlNodeList bloglist = blogx.Blogs.SelectNodes("//blogs/blog");
				XmlElement recentblog, blogel;
				foreach(XmlNode blog in bloglist) {
					blogel = (XmlElement) blog;
					recentblog = addBlog(blogel, topicname);
					unsorted.AppendChild(recentblog);
				}
			}
			// sort and save the bloglist
			sortBlogs(recent, "//blogs/recent/unsorted/blog", "@date");
			recent.RemoveChild(unsorted);
			_blogspace.Save(pathSpace(name));
		}
		/// <summary>Only specified number of recent blogs</summary>
		public void Recent(string name, int top) {
		}
		/// <summary>Only specified number of recent blogs in specified topic</summary>
		public void Recent(string name, string topic, int top) {
			x_blog topicblog = new x_blog(_application, name, topic);
		}
		
		/// <overloads>Add a blog to the recent blogs list.</overloads>
		/// <summary>All within a topic</summary>
		public void AddRecent(XmlElement blogel, string topic) {
			XmlElement recentblog = addBlog(blogel, topic);
			addBlogToList(recentblog);
			_blogspace.Save(pathSpace(Name));
		}
		/// <summary>One within a topic</summary>
		public void AddRecent(XmlElement blogel, string topic, int id) {
			XmlElement recentblog = (XmlElement) _blogspace.SelectSingleNode("//blogs/recent/blog[@topic='"+topic+"' and @id='"+id.ToString()+"']");
			addBlog(blogel, recentblog, topic);
			addBlogToList(recentblog);
			_blogspace.Save(pathSpace(Name));
		}
		
		/// <overloads>Get the most commented blogs and creates a sorted list (by comment count) in the blogspace.</overloads>
		/// <summary>Blogspace already loaded</summary>
		public void Comment() {
			Comment(Name);
		}
		/// <summary>Load blogspace</summary>
		public void Comment(string name) {
			x_blog blogx = new x_blog(_application);
			_Load(name);
			XmlNode comment = _blogspace.SelectSingleNode("//blogs/comment");
			if (comment == null)	// add it if it doesn't exist
				comment = _blogspace.SelectSingleNode("//blogs").AppendChild(_blogspace.CreateElement("comment"));
			comment.RemoveAll();
			XmlNode unsorted = comment.AppendChild(_blogspace.CreateElement("unsorted"));
			XmlNodeList topics = _blogspace.SelectNodes("//topics/topic");
			XmlElement topicel;
			string topicname;
			foreach(XmlNode topic in topics) {
				topicel = (XmlElement) topic;
				topicname = topicel.GetAttribute("name");
				blogx.Gets(name, topicname, "");
				XmlNodeList commentlist, bloglist = blogx.Blogs.SelectNodes("//blogs/blog");
				XmlElement commentblog, blogel;
				foreach(XmlNode blog in bloglist) {
					blogel = (XmlElement) blog;
					commentlist = blogel.SelectNodes("comments/comment");
					if (commentlist.Count > 0) {
						commentblog = addBlog(blogel, topicname);
						commentblog.SetAttribute("comments", commentlist.Count.ToString());
						unsorted.AppendChild(commentblog);
					}
				}
			}
			// sort and save the bloglist
			sortBlogs(comment, "//blogs/comment/unsorted/blog", "@comments");
			comment.RemoveChild(unsorted);
			_blogspace.Save(pathSpace(name));
		}
		
		/// <overloads>Sorts the most recent and most commented blogs.</overloads>
		/// <summary>Blogspace already loaded</summary>
		public void Index() {
			Index(Name);
		}
		/// <summary>Load blogspace</summary>
		public void Index(string name) {
			CountBlogs(name);
			Recent(name);
			Comment(name);
		}
		
		/// <summary>Add a comment to the comment list.</summary>
		public void AddComment(XmlElement blogel, string topic, int id) {
			XmlElement commentblog = (XmlElement) _blogspace.SelectSingleNode("//blogs/comment/blog[@topic='"+topic+"' and @id='"+id.ToString()+"']");
			if (commentblog == null) {
				XmlNode comment = _blogspace.SelectSingleNode("//blogs/comment");
				commentblog = addBlog(blogel, topic);
				comment.AppendChild(commentblog);
			}
			commentblog.SetAttribute("comments", blogel.SelectNodes("//comment").Count.ToString());
			_blogspace.Save(pathSpace(Name));
		}
		
		/// <summary>Adds a new link to the blogspace.</summary>
		public void AddLink(string name, string text, string url) {
			_Load(name);
			XmlNode linksnode = _blogspace.SelectSingleNode("//links");
			XmlElement linkelem = _blogspace.CreateElement("link");
			XmlElement lastlink = (XmlElement) linksnode.LastChild;
			linkelem.SetAttribute("id", (lastlink == null)? "1" : (Convert.ToInt32(lastlink.GetAttribute("id"))+1).ToString());
			linkelem.SetAttribute("url", url);
			linkelem.InnerText = text;
			linksnode.AppendChild(linkelem);
			_blogspace.Save(pathSpace(Name));
		}
		
		/// <summary>Edits an existing link in the blogspace.</summary>
		public void EditLink(string name, int id, string text, string url) {
			_Load(name);
			XmlNode linksnode = _blogspace.SelectSingleNode("//links");
			XmlElement linkelem = (XmlElement) linksnode.SelectSingleNode(String.Concat("link[@id=", id.ToString(), "]"));
			if (linkelem != null) {
				linkelem.SetAttribute("url", url);
				linkelem.InnerText = text;
				_blogspace.Save(pathSpace(Name));
			}
		}
		
		/// <summary>
		/// Deletes an existing link from the blogspace.
		/// </summary>
		public void DeleteLink(string name, int id) {
			_Load(name);
			XmlNode linksnode = _blogspace.SelectSingleNode("//links");
			XmlNode linknode = linksnode.SelectSingleNode(String.Concat("link[@id=", id.ToString(), "]"));
			if (linknode != null) {
				linksnode.RemoveChild(linknode);
				_blogspace.Save(pathSpace(Name));
			}
		}
		
		/// <overloads>Adds an image reference</overloads>
		/// <summary>In the blogspace blog.</summary>
		public void AddImage(string name, string filename) {
			_Load(name);
			int last = filename.LastIndexOf("\\");
			string imgname = (last >= 0)? filename.Substring(last) : filename;
			XmlNode spacenode = _blogspace.SelectSingleNode("/blogspace");
			XmlNode blognode = spacenode.SelectSingleNode("blog");
			if (blognode != null) {
				XmlNode imgnode = blognode.SelectSingleNode("image");
				if (imgnode == null)
					imgnode = blognode.AppendChild(_blogspace.CreateElement("image"));
				imgnode.InnerText = imgname;
			} else {
				addBlog(spacenode, "", "", imgname, "");
			}
			_blogspace.Save(pathSpace(Name));
		}
		/// <summary>In the topic blog.</summary>
		public void AddImage(string name, string topic, string filename) {
			_Load(name);
			checkisTopic(topic);
			int last = filename.LastIndexOf("\\");
			string imgname = (last >= 0)? filename.Substring(last) : filename;
			XmlNode topicnode = _blogspace.SelectSingleNode("//topics/topic[@name='"+topic+"']");
			XmlNode blognode = topicnode.SelectSingleNode("blog");
			if (blognode != null) {
				XmlNode imgnode = blognode.SelectSingleNode("image");
				if (imgnode == null)
					imgnode = blognode.AppendChild(_blogspace.CreateElement("image"));
				imgnode.InnerText = imgname;
			} else {
				addBlog(topicnode, "", "", imgname, "");
			}
			_blogspace.Save(pathSpace(Name));
		}
		
		/// <overloads>Adds a description blog.</overloads>
		/// <summary>To the blogspace</summary>
		public void AddBlog(string name, string author, string title, string image, string html) {
			_Load(name);
			XmlNode blognode = _blogspace.SelectSingleNode("/blogspace/blog");
			if (blognode != null)
				blogspace.RemoveChild(blognode);
			// create and populate new blog
			x_blog blogx = new x_blog();
			blogx.Add(_blogspace, blogspace, author, title, image, html);
			_blogspace.Save(pathSpace(Name));
		}
		/// <summary>To the topic</summary>
		public void AddBlog(string name, string topic, string author, string title, string image, string html) {
			_Load(name);
			checkisTopic(topic);
			XmlNode topicnode = _blogspace.SelectSingleNode("//topics/topic[@name='"+topic+"']");
			addBlog(topicnode, author, title, image, html);
			_blogspace.Save(pathSpace(Name));
		}
		
		/// <overloads>Gets the description blog.</overloads>
		/// <summary>From the blogspace</summary>
		public void GetBlog(string name) {
			_Load(name);
			XmlNode blognode = _blogspace.SelectSingleNode("/blogspace/blog");
			if (blognode != null) {
				blogspace = (XmlElement) blognode;
			} else {
				throw(new x_exception("error_blogexist", "blogspace has no blog"));
			}
		}
		/// <summary>From the topic</summary>
		public void GetBlog(string name, string topic) {
			_Load(name);
			checkisTopic(topic);
			XmlNode topicnode = _blogspace.SelectSingleNode("//topics/topic[@name='"+topic+"']");
			XmlNode blognode = topicnode.SelectSingleNode("blog");
			if (blognode != null) {
				blogspace = (XmlElement) blognode;
			} else {
				throw(new x_exception("error_blogexist", "blogspace topic has no blog"));
			}
		}
		
		/// <summary>Adds a blog count to each topic.</summary>
		public void CountBlogs(string name) {
			_Load(name);
			x_blog topicblog = new x_blog(_application);
			foreach (XmlNode topicnode in _blogspace.SelectNodes("//topics/topic")) {
				XmlElement topicel = (XmlElement) topicnode;
				string topic = topicel.GetAttribute("name");
				int count = topicblog.Count(name, topic);
				topicel.SetAttribute("count", count.ToString());
			}
			_blogspace.Save(pathSpace(Name));
		}
		
		/// <overloads>Removes the users node.</overloads>
		/// <summary>Load blogspace</summary>
		public void RemoveUsers(string name) {
			_Load(name);
		}
		/// <summary>Blogspace already loaded</summary>
		public void RemoveUsers() {
			if (_blogspace == null)
				throw(new x_exception("error_loadblogspace", "blogspace not loaded"));
			XmlNode users = _blogspace.SelectSingleNode("//users");
			if (users != null)
				users.ParentNode.RemoveChild(users);
		}
		
		/// <summary>Removes the archives node.</summary>
		/// <summary>Load blogspace</summary>
		public void RemoveArchives(string name) {
			_Load(name);
		}
		/// <summary>Blogspace already loaded</summary>
		public void RemoveArchives() {
			if (_blogspace == null)
				throw(new x_exception("error_loadblogspace", "blogspace not loaded"));
			XmlNode archives = _blogspace.SelectSingleNode("//archives");
			if (archives != null)
				archives.ParentNode.RemoveChild(archives);
		}
		
		/// <summary>Removes the blogs node.</summary>
		/// <summary>Load blogspace</summary>
		public void RemoveBlogs(string name) {
			_Load(name);
		}
		/// <summary>Blogspace already loaded</summary>
		public void RemoveBlogs() {
			if (_blogspace == null)
				throw(new x_exception("error_loadblogspace", "blogspace not loaded"));
			XmlNode blogs = _blogspace.SelectSingleNode("//blogs");
			if (blogs != null)
				blogs.ParentNode.RemoveChild(blogs);
		}
		
		/// <overloads>Removes the recent blogs node.</overloads>
		/// <summary>Load blogspace</summary>
		public void RemoveRecent(string name) {
			_Load(name);
		}
		/// <summary>Blogspace already loaded</summary>
		public void RemoveRecent() {
			if (_blogspace == null)
				throw(new x_exception("error_loadblogspace", "blogspace not loaded"));
			XmlNode blogs = _blogspace.SelectSingleNode("//blogs/recent");
			if (blogs != null)
				blogs.ParentNode.RemoveChild(blogs);
		}
		
		/// <overloads>Removes the comment blogs node.</overloads>
		/// <summary>Load blogspace</summary>
		public void RemoveComment(string name) {
			_Load(name);
		}
		/// <summary>Blogspace already loaded</summary>
		public void RemoveComment() {
			if (_blogspace == null)
				throw(new x_exception("error_loadblogspace", "blogspace not loaded"));
			XmlNode blogs = _blogspace.SelectSingleNode("//blogs/comment");
			if (blogs != null)
				blogs.ParentNode.RemoveChild(blogs);
		}
		#endregion

		#region Protected methods
		/// <summary>Load the blogspace.</summary>
		protected void _Load(string name) {
			checkSpace(name);
			getSpace(name);
			getUsers();
		}
		/// <summary>Force load of the blogspace.</summary>
		protected void _ForceLoad(string name) {
			getSpace(name);
		}
		#endregion

		#region Private methods
		private void initialise(HttpApplicationState app) {
			_application = app;
			app.Lock();
			_root = (XmlDocument) app["BlogX"];
			_users = (XmlDocument) app["UserX"];
			_path = (string) app["DataPathX"];
			_blogspaces = _root.SelectSingleNode("//blogspaces");
			app.UnLock();
		}
		private string pathRoot(string name) {	return _path + "/blogx.xml"; }
		private string pathSpace(string name) {	return _path + "/Blogs/" + name + "/blogx.xml";	}
		private string pathTopic(string name, string topic) {	return _path + "/Blogs/" + name + "/" + topic + ".xml";	}
		private string pathTemplate(string name) {	return _path + "/Blogs/" + name + "/blogx.xsl";	}
		private void checkSpace(string name) {
			XmlElement blogspace = (XmlElement) _root.SelectSingleNode("//blogspaces/blogspace[@name='"+name+"']");
			if (blogspace == null)
				throw(new x_exception("error_spaceexist", String.Concat("blogspace '", name, "' does not exist")));
			if (blogspace.GetAttribute("active") != "yes")
				throw(new x_exception("error_inactive", "blogspace is not active"));
		}
		private void checkTopic(string name) {
			XmlElement topicel = (XmlElement) _blogspace.SelectSingleNode("//topics/topic[@name='"+name+"']");
			if (topicel != null)
				throw(new x_exception("error_topicexists", "blogspace topic already exists"));
		}
		private void checkisTopic(string name) {
			XmlElement topicel = (XmlElement) _blogspace.SelectSingleNode("//topics/topic[@name='"+name+"']");
			if (topicel == null)
				throw(new x_exception("error_topicexist", "blogspace topic does not exist"));
		}
		private void getSpace(string name) {
			_blogspace = new XmlDocument();
			try {
				XmlDocument tempdoc = new XmlDocument();
				tempdoc.Load(pathSpace(name));
				
				_blogspace.LoadXml(String.Concat("<blogspace>", tempdoc.DocumentElement.InnerXml, "</blogspace>"));
				blogspace = _blogspace.DocumentElement;
				blogspace.SetAttribute("name", tempdoc.DocumentElement.GetAttribute("name"));
				blogspace.SetAttribute("admin", tempdoc.DocumentElement.GetAttribute("admin"));
				blogspace.SetAttribute("active", tempdoc.DocumentElement.GetAttribute("active"));
				setType(tempdoc.DocumentElement.GetAttribute("type"));
				string istempl = tempdoc.DocumentElement.GetAttribute("template");
				istempl = (istempl != "")? istempl : "no";
				blogspace.SetAttribute("template", istempl);
			} catch (Exception e) {
				throw(new x_exception("error_getspace", String.Concat("could not load blogspace (file:'", pathSpace(name), "') - ", e.Message)));
			}
		}
		private void getTemplate(string name) {
			_blogspace.Load(pathTemplate(name));
			blogspace = _blogspace.DocumentElement;
		}
		// needed?
		private void selectBlogspace(string name) {
			if (_blogspaces == null)
				throw(new x_exception("error_loadspace", "blogspace not loaded"));
			if (name != "")
				blogspace = (XmlElement) _blogspaces.SelectSingleNode("//blogspaces/blogspace[@name='"+name+"']");
			if (blogspace == null)
				throw(new x_exception("error_loadblogspace", "blogspace not loaded"));
		}
		private void addUsers() {
			XmlElement userel, _userel;
			XmlNodeList userList = _blogspace.SelectNodes("//users/user");
			foreach (XmlNode user in userList) {
				userel = (XmlElement) user;
				_userel = (XmlElement) _users.SelectSingleNode("//users/user[@name='"+userel.GetAttribute("name")+"']");
				userel.InnerXml = _userel.InnerXml;
				userel.SetAttribute("id", _userel.GetAttribute("id"));
				userel.SetAttribute("group", _userel.GetAttribute("group"));
			}
		}
		private void getUsers() {
			XmlElement userel, _userel;
			XmlNodeList userList = _blogspace.SelectNodes("//users/user");
			foreach (XmlNode user in userList) {
				userel = (XmlElement) user;
				_userel = (XmlElement) _users.SelectSingleNode("//users/user[@name='"+userel.GetAttribute("name")+"']");
				userel.InnerText = String.Concat(_userel.SelectSingleNode("firstname").InnerText, " ", _userel.SelectSingleNode("surname").InnerText);
			}
		}
		private void addArchive(string topic, string archive) {
			XmlElement topicEl = (XmlElement) _blogspace.SelectSingleNode("//archives/topic[@name='"+topic+"']");
			if (topicEl == null) {	// add the topic group if doesn't exist
				topicEl = _blogspace.CreateElement("topic");
				topicEl.SetAttribute("name", topic);
				_blogspace.SelectSingleNode("//archives").AppendChild(topicEl);
			}
			XmlElement archiveEl = _blogspace.CreateElement("archive");
			archiveEl.SetAttribute("name", archive);
			archiveEl.InnerText = "Archive, " + DateTime.Now.ToLongDateString();
			topicEl.AppendChild(archiveEl);
		}
		private XmlElement addBlog(XmlElement blogel, string topic) {
			XmlElement blog = _blogspace.CreateElement("blog");
			addBlog(blogel, blog, topic);
			return(blog);
		}
		private void addBlog(XmlElement blogel, XmlElement blog, string topic) {
			try {
				blog.SetAttribute("date", blogel.GetAttribute("date"));
				blog.SetAttribute("id", blogel.GetAttribute("id"));
				blog.SetAttribute("topic", topic);
				//blog.SetAttribute("author", blogel.SelectSingleNode("author").InnerText);
				blog.SetAttribute("author", blogel.GetAttribute("author"));
				string authname = "";
				XmlNode authnode = blogel.SelectSingleNode("author");
				if (authnode != null) {
					authname = blogel.SelectSingleNode("author").InnerText;
					authname = String.Concat(authname.Substring(0, 1), authname.Substring(authname.IndexOf(" ")+1, 1));
				}
				blog.SetAttribute("initials", authname);
				blog.InnerText = blogel.SelectSingleNode("title").InnerText;
			} catch(System.Exception e) {
				throw(new x_exception("error_blog", String.Concat(e.Message, "::", blog.OuterXml)));
			}
		}
		/// <summary>addBlog: Adds the topic description blog.</summary>
		private void addBlog(XmlNode topicnode, string author, string title, string image, string html) {
			XmlNode blognode = topicnode.SelectSingleNode("blog");
			if (blognode != null)
				topicnode.RemoveChild(blognode);
			// create and populate new blog
			x_blog blogx = new x_blog();
			blogx.Add(_blogspace, topicnode, author, title, image, html);
		}
		private void addBlogToList(XmlElement blogel) {
			XmlNode recent = _blogspace.SelectSingleNode("//blogs/recent");
			recent.PrependChild(blogel);
		}
		private void sortBlogs(XmlNode sorted, string unsorted, string sort) {
			XPathNavigator nav = _blogspace.CreateNavigator();
			XPathExpression expr = nav.Compile(unsorted);
			expr.AddSort(sort, XmlSortOrder.Descending, XmlCaseOrder.None, "", XmlDataType.Text);	// Sort the selected...
			
			//Display the selection.
			XmlElement newblog;
			XPathNavigator navattr, navel;
			XPathNodeIterator iterator = nav.Select(expr);
			while (iterator.MoveNext()){
				newblog = (XmlElement) _blogspace.CreateElement("blog");
				// add attributes
				navattr = iterator.Current.Clone();
				navattr.MoveToFirstAttribute();
				newblog.SetAttribute(navattr.Name, navattr.Value);
				while (navattr.MoveToNextAttribute())
					newblog.SetAttribute(navattr.Name, navattr.Value);
				// add text
				navel = iterator.Current.Clone();
				navel.MoveToFirstChild();
				newblog.InnerText = navel.Value;
				
				sorted.AppendChild(newblog);
			}
		}
		private void removeTopic(string name) {
			XmlNode topics = _blogspace.SelectSingleNode("//topics");
			XmlNode topic = topics.SelectSingleNode("topic[@name='"+name+"']");
			if (topic != null)
				topics.RemoveChild(topic);
		}
		
		/// <overloads>Sets the type of blog space</overloads>
		/// <summary>
		/// Set the current blogspace
		/// </summary>
		/// <param name="type">Name of type</param>
		private void setType(string type) {
			setType(blogspace, type);
		}
		/// <summary>
		/// Set a blogspace element
		/// </summary>
		/// <param name="space">Blogspace element</param>
		/// <param name="type">Name of type</param>
		private void setType(XmlElement space, string type) {
			switch (type) {
				case "text":	_type = x_blogspaceType.text;	break;
				case "xml":		_type = x_blogspaceType.xml;	break;
				case "html":	_type = x_blogspaceType.html;	break;
				case "custom":	_type = x_blogspaceType.custom;	break;
				case "press":	_type = x_blogspaceType.press;	break;
				case "photo":	_type = x_blogspaceType.photo;	break;
				case "image":	_type = x_blogspaceType.image;	break;
				default:		_type = x_blogspaceType.none;
								throw(new x_exception("error_type", String.Concat("no blogspace of type '", type, "'")));
			}
			space.SetAttribute("type", _type.ToString());
		}
		
		/// <overloads>Gets the type of blog space</overloads>
		/// <summary>
		/// Get the type of the current blogspace
		/// </summary>
		/// <param name="type">Name of type</param>
		private string getType(string type) {
			setType(blogspace, type);
			return _type.ToString();
		}
		#endregion
	}
}
